﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using DcsGauges.Gui.Rendering;
using DcsGauges.Gui.Gauges.Interfaces;
using DcsGauges.DataStructures;
using Microsoft.Xna.Framework.Graphics;
using DcsGauges.Input.Processors;
using Microsoft.Xna.Framework;
using DcsGauges.Shared.DataStructures;
using System.Diagnostics;

namespace DcsGauges.Gui.Gauges.Trigger
{
    class ThreeStateToggleSwitchGauge : GuiRenderableBase, ITriggerGauge
    {
        #region Inner Types

        public enum SwitchState
        {
            State0,
            State1,
            State2,
        }

        public enum Orientation
        {
            Vertical,
            Horizontal,
        }

        #endregion


        #region Fields & Properties

        private Texture2D                                           state0Texture                       = null;
        private Texture2D                                           state1Texture                       = null;
        private Texture2D                                           state2Texture                       = null;
        private SwipeProcessor                                      inputProcessor                      = null;
        private Orientation                                         orientation                         = Orientation.Vertical;
        private SwipeProcessor.SwipeEventArgs.SwipeDirections       nextStateDirection                  = SwipeProcessor.SwipeEventArgs.SwipeDirections.Negative;
        private IGaugeValueConverter<float, SwitchState>            converter                           = null;

        #endregion


        #region Constructors

        public ThreeStateToggleSwitchGauge(GuiRenderableBase parent, URect area, DcsProperty property,
            Texture2D state0Texture, Texture2D state1Texture, Texture2D state2Texture, 
            IGaugeValueConverter<float, SwitchState> converter,
            Orientation orientation = Orientation.Vertical) : base(parent, area)
        {
            this.state0Texture                  = state0Texture;
            this.state1Texture                  = state1Texture;
            this.state2Texture                  = state2Texture;
            this.orientation                    = orientation;
            this.DataProperty                   = property;
            this.converter                      = converter;

            if (this.orientation == Orientation.Vertical)
            {
                this.inputProcessor             = new VerticalSwipeProcessor(this);
                this.nextStateDirection         = SwipeProcessor.SwipeEventArgs.SwipeDirections.Negative;
            }
            else
            {
                this.inputProcessor             = new HorizontalSwipeProcessor(this);
                this.nextStateDirection         = SwipeProcessor.SwipeEventArgs.SwipeDirections.Positive;
            }

            this.inputProcessor.SwipeEvent      += new EventHandler<SwipeProcessor.SwipeEventArgs>(HandleInputProcessorSwipeEvent);
        }

        #endregion


        #region Interface Implemenation

        #region Events

        public event EventHandler<TriggerValueEventArgs>  TriggerEvent                  = delegate { };

        #endregion

        #region Properties

        public Shared.DataStructures.DcsProperty DataProperty
        {
            get;
            private set;
        }


        public float Value
        {
            get; set;
        }

        #endregion

        #endregion


        #region Overrides

        public override void Render(SpriteBatch spriteBatch, GraphicsDevice device, Rectangle parentRect, float globalZIndex)
        {
            SwitchState currentState                = this.converter.Convert(this.Value);
            Texture2D switchTexture                 = null;
            switch(currentState)
            {
                case SwitchState.State0: switchTexture = this.state0Texture; break;
                case SwitchState.State1: switchTexture = this.state1Texture; break;
                case SwitchState.State2: switchTexture = this.state2Texture; break;
                default: switchTexture = state0Texture; break;
            }

            Rectangle? originalRect                 = this.keepAspectRatio ? switchTexture.Bounds : new Rectangle?();
            Rectangle viewportRect                  = base.CalcViewportRect(parentRect, originalRect);

            spriteBatch.Draw(switchTexture, viewportRect, null, Color.White, 0.0f, Vector2.Zero, SpriteEffects.None, base.NextFrontIndex(globalZIndex, 1));
        }


        public override void InjectMouseEvent(DcsGauges.Input.Events.MouseEvent evt, Microsoft.Xna.Framework.GameTime gt)
        {
            this.inputProcessor.InjectMouseEvent(evt, gt);
        }

        #endregion


        #region Event Handlers

        private void HandleInputProcessorSwipeEvent(object sender, SwipeProcessor.SwipeEventArgs e)
        {
            Debug.Assert(this.orientation == Orientation.Vertical && e.Axis == SwipeProcessor.SwipeAxis.Vertical || 
                this.orientation == Orientation.Horizontal && e.Axis == SwipeProcessor.SwipeAxis.Horizontal, 
                "Invalid swipe axis received. Please check that input processor initialization is correct in the constructor of this class.");

            this.FireTriggerEvent(e);
        }

        #endregion


        #region Helpers & Internals

        private void FireTriggerEvent(SwipeProcessor.SwipeEventArgs e)
        {
            SwitchState currentState                = this.converter.Convert(this.Value);
            float oldValue                          = this.Value;
            if (e.Direction == this.nextStateDirection)
            {
                switch(currentState)
                {
                    case SwitchState.State0: this.Value = this.converter.ConvertBack(SwitchState.State1); break;
                    case SwitchState.State1: this.Value = this.converter.ConvertBack(SwitchState.State2); break;
                }
            }
            else
            {
                switch(currentState)
                {
                    case SwitchState.State2: this.Value = this.converter.ConvertBack(SwitchState.State1); break;
                    case SwitchState.State1: this.Value = this.converter.ConvertBack(SwitchState.State0); break;
                }
            }

            if (oldValue != this.Value)
            {
                this.TriggerEvent(this, new TriggerValueEventArgs(this.DataProperty, oldValue, this.Value));
            }
            else
            {
                Debug.Assert(false, "Trigger event didn't result in new gauge value. It seems no state change was triggered by a input event. Please check triggering code");
            }
        }

        #endregion
    }
}
